#include <myhead.h>
#include "client.h"
#include "print.h"
#include "mytime.h"

//客户端的套接字文件
static int _sk;

//用户异步转同步消息的收发
static char _sendRetBuf[REC_BUF_LEN];//用于send数据后的同步收消息
static pthread_cond_t _sendRetCond;
static pthread_mutex_t _sendRetMutex;//发消息-收消息同步锁
static pthread_mutex_t _sendSendMutex;//发发互斥锁，同时只能有一个线程在发消息


//TODO 经常要改
static int _log_level=0;//0-不打印， 1-打印
static int _print_cr=PRINT_CR;
static int _timeout = 5;//单位，秒

//心跳线程
int loginedFlag=0; //1-已经登录； 0-退出登录
char loginedName[20];
int heartTaskInterval=3;

int client_init(char* ip, int port){
	//清空并初始化全局变量
	memset(&_sendRetBuf,0,sizeof(_sendRetBuf));

	if(pthread_mutex_init(&_sendSendMutex, NULL)!=0){
		perror("pthread_mutex_init error: _sendSendMutex");
		return -1;
	}

	if(pthread_mutex_init(&_sendRetMutex, NULL)!=0){
		perror("pthread_mutex_init error: _sendRetMutex");
		return -1;
	}
	if(pthread_cond_init(&_sendRetCond, NULL)!=0){
		perror("pthread_cond_init error: _sendRetMutex");
		return -1;
	}

	//初始化通信部分
	_sk=socket(AF_INET, SOCK_DGRAM, 0);                                      
	if(_sk == -1){
		perror("socket error");
		return -1;
	}

	//connect下， 以便后续的通讯中， 直接使用recv和send进行收发报文
	struct sockaddr_in serverAddr;
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=htons(port);
	serverAddr.sin_addr.s_addr=inet_addr(ip);

	if(connect(_sk, (struct sockaddr*)&serverAddr, sizeof(serverAddr))==-1){
		perror("connect error");
		return -1;
	}

	//创建数据接收线程
	pthread_t tid;
	if(pthread_create(&tid, NULL, client_recv_task, NULL)==-1){
		perror("pthread_create error");                 
		return -1;
	}
	pthread_detach(tid);

	//心跳线程
	memset(loginedName, 0 , sizeof(loginedName));            
	pthread_t tid2;
	pthread_create(&tid2, NULL, client_heart_task , NULL);
	pthread_detach(tid2);



	return 0;
}

//异步收消息                              
void* client_recv_task(void* arg){
    while(1){
        char buf[REC_BUF_LEN]={0};
		char errmsg[1024]={0};
        ssize_t cnt=recv(_sk, buf, sizeof(buf), 0);
		if(_log_level == 1){
			printf("%s 收到[%ld]\n", mytime_now(), cnt);
			print_hex(buf, cnt, PRINT_CR);
		}
		if(cnt == -1){
			print_red("服务端异常关闭，客户端也即将退出");
			kill(getpid(), SIGKILL);
			return NULL;
		}
		//报文校验：报文头，报文长度，报文尾 都OK认为是合法的报文
		if(pkg_buf_check(buf, cnt,errmsg) != 0 || cnt <= 0){
			fprintf(stderr,"返回的报文不合法[%ld],错误原因:[%s]\n", cnt, errmsg);
			continue;
		}
		int msg_type=0;
		memcpy(&msg_type, buf+1, sizeof(int));

		//根据报文类型，分别处理报文
		if(msg_type == MSG_TYPE_RET){//通知，解锁 sendAndRet
			memcpy(_sendRetBuf, buf, cnt);
			pthread_cond_broadcast(&_sendRetCond);
			continue;
		}
		if(msg_type == MSG_TYPE_SYSTEM){//直接打印
			//系统消息：直接打印！
			pkg_t* pkg=pkg_create();
			pkg_unpack(buf, pkg);
			pkg_free(pkg);
			continue;
		}
		if(msg_type == MSG_TYPE_FRD_CHAT){//直接打印
			pkg_t* pkg=pkg_create();
			pkg_unpack(buf,pkg);
			//
			print_green("收到好友消息");
			printf("收到来自[%s]的消息:%s\n", pkg_get(pkg, 0), pkg_get(pkg, 2));

			pkg_free(pkg);
			continue;
		}
		if(msg_type == MSG_TYPE_GRP_CHAT){//直接打印
			pkg_t* pkg=pkg_create();
			pkg_unpack(buf,pkg);
			//
			print_green("收到群聊消息");
			printf("群[%s]的用户[%s]发来消息:%s\n", pkg_get(pkg, 1), pkg_get(pkg, 0), pkg_get(pkg, 2));
			pkg_free(pkg);
			continue;
		}
    }
}


void* client_heart_task(void* arg){
    //该线程一旦启动，永不退出
    while(1){
        sleep(heartTaskInterval);
        if(loginedFlag == 0){
            continue;//未登录
        }
        if(strlen(loginedName)==0){
            continue;//可能因为某种错误，登录了，但是用户名没有记录，这也不行！
        }
        //组报心跳报文
        char sendBuf[100]={0};
		int len=pkg_pack_buf(sendBuf, MSG_TYPE_HEART, 1, loginedName);

        //发送心跳报文(心跳无返回报文)
        char errmsg[200];
		pkg_t* retPkg=pkg_create();
        int retno=client_sendAndRet(sendBuf, len, retPkg,errmsg);
        if(retno==0){
			if(strcmp(pkg_get(retPkg, 0), "OK")!=0){
                loginedFlag=0;
                memset(loginedName, 0 , sizeof(loginedName));
                //与界面无法解耦的系统
                printf("\n");//我就是要单独一行打印
                print_red(pkg_get(retPkg, 1));
				client_stop_heart();
                //为了简单，直接结束进程
                //exit(EXIT_SUCCESS);
                kill(getpid(), SIGKILL);
            }
        }
		pkg_free(retPkg);
    }
}

const char* client_loginname(){
    return loginedName;
}                                                               

void client_start_heart(char *username){
	strcpy(loginedName, username);
	loginedFlag=1;
}

void client_stop_heart(){
	//strcpy(loginedName, "");
	loginedFlag=0;
}
/////////////////////////////////////////////////////////////////////////////
static int sendAndRet(char* sendBuf, int len, pkg_t* retPkg, char* errmsg){
	ssize_t cnt=send(_sk, sendBuf, len, 0);
	if(_log_level == 1){
		printf("%s 发送[%ld]:\n", mytime_now(), cnt);
		print_hex(sendBuf, cnt, _print_cr);
	}
	//异步转同步等待回复
	struct timespec abstime;
	clock_gettime(CLOCK_REALTIME, &abstime);
	abstime.tv_sec += _timeout;//超时时间，设置为5秒

	pthread_mutex_lock(&_sendRetMutex);
	int cRet=pthread_cond_timedwait(&_sendRetCond, &_sendRetMutex, &abstime);
	pthread_mutex_unlock(&_sendRetMutex);
	if(cRet == 0){//正确返回： recv线程，cond_broadcast
		strcpy(errmsg, "通信正常");
		//返回值拆包
		pkg_unpack(_sendRetBuf, retPkg);
		return 0;
	}
	if(cRet == ETIMEDOUT){//超时
		if(_log_level == 1)
			printf("通讯超时,cRet=[%d]:\n", cRet);
		strcpy(errmsg, "通信超时");
		return -1;
	}
	strcpy(errmsg, "通信中碰到了未知的错误");
	return -1;
}

int client_sendAndRet(char* sendBuf, int len, pkg_t* retPkg, char* errmsg){
	int retno=0;
	pthread_mutex_lock(&_sendSendMutex);
	retno=sendAndRet(sendBuf,len,retPkg,errmsg);
	pthread_mutex_unlock(&_sendSendMutex);
	return retno;
	
}

/*
int mynet_send_locked(char* buf,int len, spkg_ret_t* ret, int repeatTimes, int onceTimeout,char* errmsg){
	int retno=0;
	pthread_mutex_lock(&_sendMutex);
	retno=mynet_send(buf,len,ret,repeatTimes,onceTimeout,errmsg);
	pthread_mutex_unlock(&_sendMutex);
	return retno;
}
*/

/*
static int mynet_send(char* sendBuf, int sSize, spkg_ret_t* ret, int repTimes, int timeout, char* errmsg){
	//添加帧号
	sendBuf[2]=_frame_no;
	//
	//等待回复:超时重发
	int cRet=0;
	for(int i=0;i<repTimes;i++){
		//清空_retPkg全局变量	
		memset(&_retPkg, 0, sizeof(_retPkg));
		ssize_t cnt=send(_sk, sendBuf, sSize,0);
		if(net_log_level == 1){
			printTime();
			printf(" 第[%d/%d]次发送[%ld]:\n",i+1, repTimes, cnt);
			printHex(sendBuf, cnt, 16);
		}
		struct timespec abstime;
		clock_gettime(CLOCK_REALTIME, &abstime);
		abstime.tv_sec += timeout;//超时时间，设置为5秒

		pthread_mutex_lock(&_retMutex);
		cRet=pthread_cond_timedwait(&_retCond, &_retMutex, &abstime);
		pthread_mutex_unlock(&_retMutex);
		if(cRet == 0){//通信成功，至少服务器有返回
			memcpy(ret, &_retPkg.retinfo, sizeof(spkg_ret_t));
			return 0;
		}
		if(net_log_level == 1)
			printf("第[%d/%d]次通讯超时,cRet=[%d]:\n", i+1,repTimes, cRet);
	}
	if(cRet == ETIMEDOUT){
		//超时
		strcpy(errmsg, "通信超时");
		return -1;
	}
	strcpy(errmsg, "未知错误");
	return -1;
}

int mynet_send_locked(char* buf,int len, spkg_ret_t* ret, int repeatTimes, int onceTimeout,char* errmsg){
	int retno=0;
	pthread_mutex_lock(&_sendMutex);
	retno=mynet_send(buf,len,ret,repeatTimes,onceTimeout,errmsg);
	pthread_mutex_unlock(&_sendMutex);
	return retno;
}
*/

